﻿using System;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Resources;
using System.Xml;
using System.Xml.Linq;

namespace TTApp.Phone.Silverlight.Hexagons
{
    /// <summary>
    /// Chuyển dữ liệu BoardSource sang/từ dạng khác.
    /// </summary>
    public sealed class BoardSourceConverter : TypeConverter
    {
        #region //----------------------------- Overridden Methods ----------------------------------------//
        /// <summary>
        /// Kiểm tra xem có thể convert từ kiểu sourceType hay không.
        /// </summary>
        /// <param name="context">không biết cái này làm gì @@!</param>
        /// <param name="sourceType"></param>
        /// <returns>true nếu sourceType là kiểu string.</returns>
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            if (sourceType == typeof(string))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// Nếu đầu vào là 1 đường dẫn đến 1 file xml thì đọc file xml. Nếu cấu trúc đúng thì chuyển nội dung
        /// file này sang đối tượng BoardSource.
        /// </summary>
        /// <param name="context">Không biết :|</param>
        /// <param name="culture">Không biết :|</param>
        /// <param name="value">Nếu là đường dẫn đến 1 file xml thì đọc file, convert nội dung sang Board nếu có thể.</param>
        /// <returns>Đối tượng BoardSource dùng làm nguồn cho Board. Trả về null nếu convert không thành công.</returns>
        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            BoardSource source = null;

            // Đường dẫn đến file xml. Mặc định là ở TTApp.Phone.Silverlight.Hexagons.dll
            Uri uri = new Uri(String.Format("/TTApp.Phone.Silverlight;component/{0}", value), UriKind.Relative);
            StreamResourceInfo resourceInfo = Application.GetResourceStream(uri);

            // Tạo đối tượng LINQ to XML
            XDocument document = XDocument.Load(resourceInfo.Stream);

            XElement rootElement = document.Root;
            if (rootElement.Name.LocalName == "MapData")
            {
                source = SetBoardSource(rootElement);
            }
            else
            {
                throw new XmlException("Gốc phải là tag <MapData>");
            }

            return source;
        }
        #endregion
        
        #region //----------------------------- Class Private Methods -------------------------------------//
        #region //-------------- Attribute --------------//
        /// <summary>
        /// Gán thuộc tính cho BackgroundDecoration.
        /// </summary>
        /// <param name="decorationElement">Element BackgroundDecoration trong file xml.</param>
        /// <param name="backgroundDecoration">Đối tượng BackgroundDecoration được gán thuộc tính.</param>
        private void SetBackgroundDecorationAttribute(XElement decorationElement, BackgroundDecoration backgroundDecoration)
        {
            foreach (XAttribute attribute in decorationElement.Attributes())
            {
                switch (attribute.Name.LocalName)
                {
                    case "Name":
                        backgroundDecoration.DecorationName = attribute.Value;
                        break;
                }
            }
        }

        /// <summary>
        /// Gán thuộc tính cho source.
        /// </summary>
        /// <param name="rootElement">Nút gốc trong file xml.</param>
        /// <param name="slottedBoard">Nguồn của board.</param>
        private void SetSlottedBoardAttribute(XElement rootElement, SlottedBoard slottedBoard)
        {
            // Gán thuộc tính cho source (SlottedBoard)
            foreach (XAttribute attribute in rootElement.Attributes())
            {
                switch (attribute.Name.LocalName)
                {
                    case "XNumber":
                        slottedBoard.HorizontalHexagonSlotNumber = Convert.ToInt32(attribute.Value);
                        break;

                    case "YNumber":
                        slottedBoard.VerticalHexagonSlotNumber = Convert.ToInt32(attribute.Value);
                        break;
                }
            }
        }

        /// <summary>
        /// Gán thuộc tính cho HexagonSlot.
        /// </summary>
        /// <param name="slotElement">Element HexagonSlot trong file xml.</param>
        /// <param name="hexagonSlot">Đối tượng HexagonSlot được gán thuộc tính.</param>
        private void SetHexagonSlotAttribute(XElement slotElement, HexagonSlot hexagonSlot)
        {
            foreach (XAttribute attribute in slotElement.Attributes())
            {
                switch (attribute.Name.LocalName)
                {
                    case "X":
                        Board.SetX(hexagonSlot, Convert.ToDouble(attribute.Value));
                        break;

                    case "Y":
                        Board.SetY(hexagonSlot, Convert.ToDouble(attribute.Value));
                        break;

                    case "Margin":
                        hexagonSlot.Margin = (Thickness)new ThicknessConverter().ConvertFrom(attribute.Value);
                        break;
                }
            }
        }

        /// <summary>
        /// Gán thuộc tính cho Hexagon.
        /// </summary>
        /// <param name="hexagonElement">Element Hexagon trong file xml.</param>
        /// <param name="hexagon">Đối tượng Hexagon được gán thuộc tính.</param>
        private void SetHexagonAttribute(XElement hexagonElement, Hexagon hexagon)
        {
        }
        #endregion

        #region //-------------- Collection -------------//
        /// <summary>
        /// Thiết lập BoardSource. Trong file xml, dữ liệu của BoardSource nằm trong tag <Board></Board>
        /// </summary>
        /// <param name="rootElement">tag <Board></Board></param>
        /// <returns>Đối tượng BoardSource chứa dữ liệu của bàn cờ.</returns>
        private BoardSource SetBoardSource(XElement rootElement)
        {
            XElement boardRootElement = XmlUtils.GetUniqueChildElement(rootElement, "Board");
            string boardType = XmlUtils.GetSingleChildName(boardRootElement);

            XElement boardElement = XmlUtils.GetUniqueChildElement(boardRootElement, boardType);

            BoardSource source = null;

            switch (boardType)
            {
                case "SlottedBoard":
                    source = new SlottedBoard();
                    SetSlottedBoard(boardElement, source);
                    break;
            }

            return source;
        }

        /// <summary>
        /// Thiết lập SlottedBoard từ slottedBoardElement.
        /// </summary>
        /// <param name="slottedBoardElement">Element có tag là <SlottedBoard></SlottedBoard></param>
        /// <param name="boardSource">Đối tượng sẽ được dùng để thiết lập giá trị.</param>
        private void SetSlottedBoard(XElement slottedBoardElement, BoardSource boardSource)
        {
            SetSlottedBoardAttribute(slottedBoardElement, boardSource as SlottedBoard);

            // Đặt HexagonSlot vào SlottedBoard
            SetSlottedBoardHexagonSlots(slottedBoardElement, boardSource as SlottedBoard);   
        }

        /// <summary>
        /// Tìm trong rootElement, nếu có HexagonSlot thì đặt nó vào slottedBoard.
        /// </summary>
        /// <param name="boardSource">Element tương ứng với BoardSource</param>
        /// <param name="slottedBoard">Nguồn cho Board, dùng để đặt HexagonSlot</param>
        private void SetSlottedBoardHexagonSlots(XElement boardSource, SlottedBoard slottedBoard)
        {
            // HexagonSlot trong Board
            foreach (XElement slotElement in boardSource.Elements())
            {
                HexagonSlot hexagonSlot = null;
                switch (slotElement.Name.LocalName)
                {
                    case "HexagonSlot":
                        hexagonSlot = new HexagonSlot();
                        break;
                    case "BrokenHexagonSlot":
                        hexagonSlot = new BrokenHexagonSlot();
                        break;
                }

                SetHexagonSlotAttribute(slotElement, hexagonSlot);

                // Hexagon trong HexagonSlot
                XElement hexagonElement = XmlUtils.GetUniqueChildElement(slotElement, "Hexagon");
                if (hexagonElement != null)
                {
                    Hexagon hexagon = new Hexagon();
                    SetHexagonAttribute(hexagonElement, hexagon);

                    hexagonSlot.Hexagon = hexagon;
                }

                // Decorations của HexagonSlot
                XElement decorationsElement = XmlUtils.GetUniqueChildElement(slotElement, "Decorations");
                if (decorationsElement != null)
                {
                    SetDecorations(decorationsElement, hexagonSlot);
                }

                slottedBoard.HexagonSlots.Add(hexagonSlot);
            }
        }

        /// <summary>
        /// Tìm trong decorationsElement, nếu có Decoration thì cho nó vào hexagonSlot.
        /// </summary>
        /// <param name="decorationsElement">Element tương ứng với Decorations collection của HexagonSlot.</param>
        /// <param name="hexagonSlot">Đối tượng HexagonSlot, dùng để đặt Decoration vào.</param>
        private void SetDecorations(XElement decorationsElement, HexagonSlot hexagonSlot)
        {
            // Xóa những decoration mặc định đi
            hexagonSlot.Decorations.Clear();

            // Từng Decoration trong Decorations
            foreach (XElement decorationElement in decorationsElement.Elements())
            {
                Decoration decoration = null;

                switch (decorationElement.Name.LocalName)
                {
                    case "BackgroundDecoration":
                        {
                            decoration = new BackgroundDecoration();
                            SetBackgroundDecorationAttribute(decorationElement, decoration as BackgroundDecoration);
                            hexagonSlot.Decorations.Add(decoration);

                            break;
                        }
                }
            }
        }
        #endregion
        #endregion
    }
}
